// CMakeProject1.cpp: 定义应用程序的入口点。
//

#include <iostream>
#include <xlnt/xlnt.hpp>
#include <set>
#include "File.h"
#include <functional>
#include "read_cfg.h"
#include "str_util.h"
#include <stdio.h>
#include <stdlib.h>
#include "check.h"
#ifndef WIN32
#include <unistd.h>
#define fopen_s(pFile,filename,mode) ((*(pFile))=fopen((filename),  (mode)))==NULL
#endif

#define LDEBUG(x, ...) printf(x "\n", ##__VA_ARGS__)
#define LERROR(x, ...) printf("[error] " x "\n", ##__VA_ARGS__)



using namespace std;
using namespace su;

static string IGNORE_CLIENT_FIELD_STRING ; //忽略客户端字段
static string EXPORT_CPP_FILE ; //导致CPP文件名前缀


Config gCfg;

map<string, std::function<void(FILE* )>> gStr2fun; //登记cpp模板代码 特殊字符替换功能函数

#if 0
void t()
{



	xlnt::workbook wb;
	wb.load(EXCEL_PATH + "/Player.xlsx");
	for (int i = 0; i < wb.sheet_count(); i++)
	{
		auto ws = wb.sheet_by_index(i);
		cout << "\n\nsheet name=" << ws.title() << "row h" << ws.highest_row() << "col h" << ws.highest_column().index << endl;


		std::set<uint32_t> ignoreCol;
		std::set<uint32_t> ignoreRow;
		for (uint32_t r = 1; r <= ws.highest_row(); r++)//row
		{
			for (uint32_t c = 1; c <= ws.highest_column().index; c++)//col
			{
				xlnt::cell cell = ws.cell(xlnt::cell_reference(c, r)); // 坐标定位
				if (c == 1 && cell.to_string().empty())
				{
					cout << "ignore row=" << cell.row() << " col=" << cell.column_index() << endl;
					ignoreRow.insert(r);
					continue;
				}
				if (r == 1 && cell.to_string().empty())
				{
					cout << "ignore row=" << cell.row() << " col=" << cell.column_index() << endl;
					ignoreCol.insert(c);
					continue;
				}
				if (ignoreCol.find(c) != ignoreCol.end())
				{
					continue;
				}
				if (ignoreRow.find(r) != ignoreRow.end())
				{
					continue;
				}
				cout << "row=" << cell.row() << " col=" << cell.column_index() << endl;
				std::clog << cell.to_string() << std::endl;
			}
		}

		for (auto row : ws.rows(true))
		{
			for (xlnt::cell cell : row)
			{
				//cout << "row=" << cell.row() <<" col=" << cell.column_index() << endl;
				//std::clog << cell.to_string() << std::endl;
			}
		}
	}

}

#endif // 0



//普通域数据 和 数组域数据 2选一
struct Field 
{
	bool bArray = false; //true表示数组域
	//普通域数据
	string name;
	string datatype; //数据类型  int int64 string double ...
	string cDataType; //int64 转 int64_t //导出代码格式
	uint32 col = 1; //在excel的列位置， 1开始

	//数组域数据
	uint32 arrayIdx; //数组索引，0开始。对应字段描述 1
};
struct SheetParser
{
	xlnt::worksheet& m_ws; //不安全，离开域后回 野
	string m_excelFileName;
	string m_sheetName;
	string m_cfgName;
	std::set<uint32_t> m_ignoreCol;
	std::set<uint32_t> m_ignoreRow;
	vector<Field> m_vecField; //域信息。按sheet列位置排序
	uint32_t m_maxCol = 0; //最大列长度，包括无效列

	SheetParser(xlnt::worksheet& _ws, const string& excelFileName)
		:m_ws(_ws)
		,m_excelFileName(excelFileName)
	{

		{//去掉后缀
			size_t pos = m_excelFileName.find('.');
			if (std::string::npos != pos)
			{
				m_excelFileName = m_excelFileName.substr(0, pos);
			}
		}
		m_sheetName = m_ws.title();
		m_cfgName = m_excelFileName + m_sheetName;
	}
	bool Build()
	{
		if (!BuildStruct())
		{
			LERROR("BuildStruct");
			exit(1);
		}

		if (!CheckStruct())
		{
			LERROR("CheckStruct");
			exit(1);
		}
		if (!BuildByte())
		{
			LERROR("BuildByte");
			exit(1);
		}
		return true;
	}	

	bool BuildStruct()
	{
		/*
id	name	type	item1	num1	item2	num2	doubleNum
活动ID	活动名	type	item1	num1	item2	num2	doubleNum
int	string	string	int	int	int	int	double
服务器，客户端区别信息	c
100	abc	typename	1	1	2	2	3.3
		*/
		//迭代前4行
		for (uint32_t c = 1; c <= m_ws.highest_column().index; c++)//col
		{
			xlnt::cell cell1 = m_ws.cell(xlnt::cell_reference(c, 1)); // 坐标定位
			xlnt::cell cell3 = m_ws.cell(xlnt::cell_reference(c, 3));
			xlnt::cell cell4 = m_ws.cell(xlnt::cell_reference(c, 4));
			if (cell4.to_string() == IGNORE_CLIENT_FIELD_STRING)
			{
				continue;
			}
			string name = cell1.to_string();
			string datatype = cell3.to_string();
			if (name.empty())
			{
				m_maxCol = c;
				break;
			}
			Field field;
			uint32 suffixLen = 0;//后缀数字长度
			{
				//支持最大2位数数组
				auto lastIt = name.end() - 1;
				if (*lastIt >= '0' && *lastIt <= '9')
				{
					suffixLen++;

					if (lastIt != name.begin())
					{
						lastIt--;
						if (*lastIt >= '0' && *lastIt <= '9')
						{
							suffixLen++;
						}
					}
				}
			}
			if (suffixLen > 0)
			{
				string arrayName(name.begin(), name.end() - suffixLen);
				string numStr(name.end() - suffixLen, name.end());
				uint32 num = su::StrNum::ToUint32(numStr);
				if (num==0)
				{
					LERROR("array suffixlen error. %s", name.c_str());
					return false;
				}
				field.bArray = true;
				field.name = arrayName;
				field.datatype = datatype;
				field.col = c;
				field.arrayIdx = num - 1;
				m_vecField.push_back(field);
			}
			else
			{
				field.name = name;
				field.datatype = datatype;
				field.col = c;
				m_vecField.push_back(field);
			}
		}

		{//init cDataType
			for (Field& field : m_vecField)
			{
				if (field.datatype == "uint")
				{
					field.cDataType = "uint32_t";
				}
				else if (field.datatype == "int64")
				{
					field.cDataType = "int64_t";
				}
				else if (field.datatype == "uint64")
				{
					field.cDataType = "uint64_t";
				}
				else if (field.datatype == "string")
				{
					field.cDataType = "std::string";
				}
				else
				{
					field.cDataType = field.datatype;
				}
			}
		}

#if 0
		struct Sheet
		{
			using Row = std::vector<std::any>;//每行数据 类型。 包括数组内容。
			std::map<std::string, int> fieldName2Idx; //域名 对应 Row类型数据的索引。 包括数组内容。 域名用 item0, item1  表达
			std::map<uint32, Row> id2Row; //表单具体数据

			void EachRow(std::function<void(const RowInfo& rowinfo)> fun) const;
		};
		//普通域数据 和 数组域数据 2选一
		struct Field
		{
			bool bArray = false; //true表示数组域
			//普通域数据
			string name;
			string datatype; //数据类型  int int64 string double ...
			string cDataType; //int64 转 int64_t //导出代码格式
			uint32 col = 1; //在excel的列位置， 1开始

			//数组域数据
			uint32 arrayIdx; //数组索引，0开始。对应字段描述 1
		};
#endif // 0

		{// init Sheet for check
			Sheet& sheet = CheckMgr::Ins().GetSheetForWrite(m_cfgName);
			int idx = 0;
			for (Field& field : m_vecField)
			{
				if (field.bArray)
				{
					sheet.fieldName2Idx[field.name+StrNum::NumToStr(field.arrayIdx)] = idx;
				}
				else
				{
					sheet.fieldName2Idx[field.name] = idx;
				}
				++idx;
			}
		}


		return true;
	}

	bool CheckStruct()
	{
		for (Field& field : m_vecField)
		{
			if (field.cDataType.empty())
			{
				LERROR("%s cDataType.empty()", m_cfgName.c_str());
				exit(1);
			}
		}

// check data type
		set<string> alltype = { "int", "uint", "int64", "uint64", "double", "float", "string" };
		for (Field& field : m_vecField)
		{
			auto it = alltype.find(field.datatype);
			if (it == alltype.end())
			{
				LERROR("unknow datatype = %s", field.datatype.c_str());
				exit(1);
			}
		}

		//check array
		map<string, string> arrayName2Type;
		for (Field& field : m_vecField)
		{
			if (!field.bArray)
			{
				continue;
			}
			string& type = arrayName2Type[field.name];
			if (type.empty())
			{
				type = field.datatype;
				continue;
			}
			if (type != field.datatype)
			{
				LERROR("array have different data type. name=%s", field.name.c_str());
				exit(1);
			}
		}
		
		bool bFindId = false;
		for (Field& field : m_vecField)
		{
			if (StrUtil::tolower(field.name) == "id")
			{
				if (field.datatype != "uint")
				{
					LERROR("%s id datatype must be uint. cur is %s", m_cfgName.c_str(), field.datatype.c_str());
					exit(1);
				}
				//if (field.col != 1)
				//{
				//	LERROR("%s id datatype must be in col 1 ", m_cfgName.c_str());
				//	exit(1);
				//}
				bFindId = true;
				break;
			}
		}
		if (!bFindId)
		{
			LERROR("%s  can't find id datatype", m_cfgName.c_str());
			exit(1);
		}
		//log
		//{
		//	string log("struct: ");
		//	for (auto& i : m_vecField)
		//	{
		//		if (i.bArray)
		//		{
		//			log.append(i.name + StrNum::NumToStr(i.arrayIdx) + "(" + i.datatype + ")");
		//		}
		//		else
		//		{
		//			log.append(i.name + "(" + i.datatype + ")");
		//		}
		//		log.append(", ");
		//	}
		//	LDEBUG("%s", log.c_str());
		//}


		return true;
	}
	bool BuildByte()
	{
		//LDEBUG("BuildByte");
		string bytes;
		bytes.reserve(1024);
		bool bBuildEnd = false;

#if 0
		struct Sheet
		{
			using Row = std::vector<std::any>;//每行数据 类型。 包括数组内容。
			std::map<std::string, int> fieldName2Idx; //域名 对应 Row类型数据的索引。 包括数组内容。 域名用 item0, item1  表达
			std::map<uint32, Row> id2Row; //表单具体数据

			void EachRow(std::function<void(const RowInfo& rowinfo)> fun) const;
		};
		//普通域数据 和 数组域数据 2选一
		struct Field
		{
			bool bArray = false; //true表示数组域
			//普通域数据
			string name;
			string datatype; //数据类型  int int64 string double ...
			string cDataType; //int64 转 int64_t //导出代码格式
			uint32 col = 1; //在excel的列位置， 1开始

			//数组域数据
			uint32 arrayIdx; //数组索引，0开始。对应字段描述 1
		};
#endif // 0

		// init Sheet for check
		Sheet& sheet = CheckMgr::Ins().GetSheetForWrite(m_cfgName);

		uint32_t rowNum = 0; //1开始
		for (auto row : m_ws.rows(true))
		{
			rowNum++;
			if (rowNum <5)
			{
				continue;
			}
			uint32_t colNum = 0;//1开始
			uint32 id = 0;
			Sheet::Row checkRow;
			auto it = m_vecField.begin();

			for (size_t m = 0; m < m_vecField.size(); m++)
			{
				xlnt::cell cell = row[m];
				colNum++;
				if (it == m_vecField.end())
				{
				//	LDEBUG("it == m_vecField.end()");
					break;
				}
				Field& i = *it;
				if (colNum != i.col)
				{ //跳过m_vecField不保护忽略的列，
				//	LDEBUG("colNum != i.col %d %d", colNum, i.col);
					continue;
				}
				else
				{
					++it;
				}

				string str = cell.to_string();
			//	LDEBUG("rowNum, colNum,i.col =%d %d %d, str=%s", rowNum, colNum, i.col, str.c_str());
				if (m == 0)
				{
					if (str.empty())
					{
						bBuildEnd = true;
						//	LDEBUG("bBuildEnd = true");
						break;
					}
					else
					{
						char check = 113;
						bytes.append((char*)&check, sizeof(check));//行校验头
					}
				}

				if (i.datatype == "int")
				{
					int32 num = StrNum::ToInt32(str);
					bytes.append((char*)&num, sizeof(num));
					checkRow.push_back(num);
				}
				else if (i.datatype == "uint")
				{
					uint32 num = StrNum::ToUint32(str);
					bytes.append((char*)&num, sizeof(num));

					if (i.name == "id")
					{
						id = num;
					}
					checkRow.push_back(num);
				}
				else if (i.datatype == "int64")
				{
					int64 num = StrNum::ToInt64(str);
					bytes.append((char*)&num, sizeof(num));
					checkRow.push_back(num);
				}
				else if (i.datatype == "uint64")
				{
					uint64 num = StrNum::ToUint64(str);
					bytes.append((char*)&num, sizeof(num));
					checkRow.push_back(num);
				}
				else if (i.datatype == "double")
				{
					double num = StrNum::StrToNum<double>(str);
					bytes.append((char*)&num, sizeof(num));
					checkRow.push_back(num);
					//{//log
						//string str = StrUtil::BinaryToHex(string(bytes.end() - sizeof(num), bytes.end()));
						//LDEBUG("double: %s", str.c_str());
					//}
				}
				else if (i.datatype == "string")
				{
					uint32 len = (uint32)str.length();
					bytes.append((char*)&len, sizeof(len));
					bytes.append(str);
					checkRow.push_back(str);
				}
				else
				{
					LERROR("unknow datatype %s", i.datatype.c_str());
					return false;
				}
			}

			if (checkRow.size() > 0)
			{
				bool bOk = sheet.id2Row.emplace(id, std::move(checkRow)).second;
				if (!bOk)
				{
					LERROR("%s have repeated id %d", m_cfgName.c_str(), id);
					return false;
				}
			}
			if (bBuildEnd)
			{
				break;
			}
		}


		//{//log
		//	string str = StrUtil::BinaryToHex(bytes);
		//	LDEBUG("allstr: %s， size=%lld", str.c_str(), bytes.length());
		//}
		{
			const char* binPath = gCfg.GetStr("BinPath");
			
			string name = binPath; 
			name.append(m_excelFileName + m_ws.title() + ".bin");
			//LDEBUG("binPath=%s", name.c_str());
			FILE* fp = nullptr;
			auto er = fopen_s(&fp, name.c_str(), "wb");
			if (er != 0)
			{
				LERROR("openfile fail %s", name.c_str());
				return false;
			}
			fwrite(bytes.c_str(), bytes.length(), 1, fp);
			fclose(fp);
		}
		return true;
	}

	//迭代所有数据，从第5行开始
#if 0
	//void EachCell(function<bool(xlnt::cell& cell, uint32_t row, uint32_t col)> cb)
	//{
	//	//排除空cell,找迭代行列
	//	for (uint32_t r = 1; r <= m_ws.highest_row(); r++)//row
	//	{
	//		for (uint32_t c = 1; c <= m_ws.highest_column().index; c++)//col
	//		{
	//			xlnt::cell cell = m_ws.cell(xlnt::cell_reference(c, r)); // 坐标定位
	//			if (m_ignoreCol.find(c) != m_ignoreCol.end())
	//			{
	//				continue;
	//			}
	//			if (m_ignoreRow.find(r) != m_ignoreRow.end())
	//			{
	//				continue;
	//			}
	//			if (c == 1 && cell.to_string().empty())
	//			{
	//				m_ignoreRow.insert(r);
	//				continue;
	//			}
	//			if (r == 1 && cell.to_string().empty())
	//			{
	//				m_ignoreCol.insert(c);
	//				continue;
	//			}
	//			if (!cb(cell, r, c))
	//			{
	//				return;
	//			}
	//		}
	//	}
	//}
#endif // 0

};


vector<SheetParser> gVecSheetParser;

void ReplaceInitCfg(FILE* cppFile)
{
	for (SheetParser& parser : gVecSheetParser)
	{
		fprintf(cppFile, "void CfgMgr::Init%s(const string& filePath)\n", parser.m_cfgName.c_str());
		fprintf(cppFile, "{\n");
		fprintf(cppFile, "	auto f = [&](const char* buf, uint32_t len)\n");
		fprintf(cppFile, "	{\n");

		fprintf(cppFile, "		uint32_t bufIdx = 0;\n");
		fprintf(cppFile, "		for (int i = 0; i < 10001; i++)\n");
		fprintf(cppFile, "		{\n");
		fprintf(cppFile, "			if (i >= 10000)\n");
		fprintf(cppFile, "			{\n");
		fprintf(cppFile, "				LERROR(\"endless loop\");\n");
		fprintf(cppFile, "				break;\n");
		fprintf(cppFile, "			}\n");
		fprintf(cppFile, "			%sCfg cfg;\n", parser.m_cfgName.c_str());
		fprintf(cppFile, "			const char* curBuf = buf + bufIdx;\n");
		fprintf(cppFile, "			{//行开头校验\n");
		fprintf(cppFile, "				char check = *(decltype(check)*)curBuf;\n");
		fprintf(cppFile, "				bufIdx += sizeof(check); curBuf = buf + bufIdx;\n");
		fprintf(cppFile, "				if (check != 113) { LERROR(\"error bin file [%s], row start check fail\", filePath.c_str()); return; }\n", "%s");
		fprintf(cppFile, "			}\n");

		for (Field& field : parser.m_vecField)
		{
			if (field.bArray == false)
			{
				if (field.datatype == "string")
				{
					fprintf(cppFile, "			{\n");
					fprintf(cppFile, "				uint32_t len = 0;\n");
					fprintf(cppFile, "				len = *(decltype(len)*)curBuf;\n");
					fprintf(cppFile, "				if(len>10000){LERROR(\"error bin, str len too max. file[%s]\", filePath.c_str()); return;}\n", "%s");
					fprintf(cppFile, "				bufIdx += sizeof(len); curBuf = buf + bufIdx;\n");
					fprintf(cppFile, "				cfg.%s.append(curBuf, len);\n", field.name.c_str());
					fprintf(cppFile, "				bufIdx += len; curBuf = buf + bufIdx;\n");
					fprintf(cppFile, "			}\n");
				}
				else
				{
					fprintf(cppFile, "			cfg.%s = *(decltype(cfg.%s)*)curBuf;\n", field.name.c_str(), field.name.c_str());
					fprintf(cppFile, "			bufIdx += sizeof(cfg.%s); curBuf = buf + bufIdx;\n", field.name.c_str());
				}
			}
			else//array code
			{
				if (field.datatype == "string")
				{
					fprintf(cppFile, "			{\n");
					fprintf(cppFile, "				uint32_t len = 0;\n");
					fprintf(cppFile, "				len = *(decltype(len)*)curBuf;\n");
					fprintf(cppFile, "				if(len>10000){LERROR(\"error bin, str len too max. file[%s]\", filePath.c_str()); return;}\n", "%s");
					fprintf(cppFile, "				bufIdx += sizeof(len); curBuf = buf + bufIdx;\n");
					fprintf(cppFile, "				cfg.%s[%d].append(curBuf, len);\n", field.name.c_str(), field.arrayIdx);
					fprintf(cppFile, "				bufIdx += len; curBuf = buf + bufIdx;\n");
					fprintf(cppFile, "			}\n");
				}
				else 
				{
					fprintf(cppFile, "			cfg.%s[%d] = *(%s*)curBuf;\n", field.name.c_str(), field.arrayIdx, field.datatype.c_str());
					fprintf(cppFile, "			bufIdx += sizeof(%s); curBuf = buf + bufIdx;\n", field.datatype.c_str());
				}
			}
		}

		fprintf(cppFile, "			m_id2%sCfg.emplace(cfg.id, cfg); \n", parser.m_cfgName.c_str());
		fprintf(cppFile, "			if (bufIdx >= len){return;}//row end\n");
		fprintf(cppFile, "		};\n");

		fprintf(cppFile, "	};\n");
		fprintf(cppFile, "	ParseFile(filePath, f);\n");
		fprintf(cppFile, "}\n");
	}

}

void ReplacegetfunDef(FILE* cppFile)
{
	for (SheetParser& parser : gVecSheetParser)
	{
		fprintf(cppFile, "const %sCfg* CfgMgr::Get%sCfg(uint32_t id) {\n", parser.m_cfgName.c_str(), parser.m_cfgName.c_str());
		fprintf(cppFile, "	auto it = m_id2%sCfg.find(id);\n", parser.m_cfgName.c_str());
		fprintf(cppFile, "	if (it == m_id2%sCfg.end()) {\n", parser.m_cfgName.c_str());
		fprintf(cppFile, "		return nullptr;\n");
		fprintf(cppFile, "	}\n");
		fprintf(cppFile, "	return &it->second;\n");
		fprintf(cppFile, "}\n");
	}

}
void ReplaceDeclareInitCfg(FILE* cppFile)
{
	for (SheetParser& parser : gVecSheetParser)
	{
		fprintf(cppFile, "	void Init%s(const std::string & filePath);\n", parser.m_cfgName.c_str());
	}
}
void ReplaceCallInitCfg(FILE* cppFile)
{
	for (SheetParser& parser : gVecSheetParser)
	{
		fprintf(cppFile, "	Init%s(binFilePath + \"%s.bin\");\n", parser.m_cfgName.c_str(), parser.m_cfgName.c_str());
	}
}

void ReplaceCfgStruct(FILE* cppFile)
{
	for (SheetParser& parser : gVecSheetParser)
	{
		fprintf(cppFile, "struct %sCfg\n", parser.m_cfgName.c_str());
		fprintf(cppFile, "{\n");

		struct ArrayInfo 
		{
			string name;
			string datatype;
			string cDataType;
			uint32_t len=0;
		};
		map<string,ArrayInfo> name2ArrayInfo;

		for (Field& field : parser.m_vecField)
		{
			if (field.bArray)
			{
				ArrayInfo& info = name2ArrayInfo[field.name];
				info.name = field.name;
				info.datatype = field.datatype;
				info.cDataType = field.cDataType;
				if (info.len<field.arrayIdx+1)
				{
					info.len = field.arrayIdx + 1;
				}
			}
			else
			{
				fprintf(cppFile, "	%s %s;\n", field.cDataType.c_str(), field.name.c_str());
			}
		}
		for (auto& i : name2ArrayInfo)
		{
			const ArrayInfo& info = i.second;
			fprintf(cppFile, "	%s %s[%d];\n", info.cDataType.c_str(), info.name.c_str(), info.len);
			
		}
		fprintf(cppFile, "};\n");
	}
}

void ReplaceGetFun(FILE* cppFile)
{
	for (SheetParser& parser : gVecSheetParser)
	{
		fprintf(cppFile, "	const %sCfg* Get%sCfg(uint32_t id);\n", parser.m_cfgName.c_str(), parser.m_cfgName.c_str());
	}
}

void ReplaceMember(FILE* cppFile)
{
	for (SheetParser& parser : gVecSheetParser)
	{
		fprintf(cppFile, "	std::map<uint32_t, %sCfg> m_id2%sCfg;\n", parser.m_cfgName.c_str(), parser.m_cfgName.c_str());
	}
}

void ReplaceGetAll(FILE* cppFile)
{
	for (SheetParser& parser : gVecSheetParser)
	{
		fprintf(cppFile, "	const std::map<uint32_t, %sCfg>& GetAll%sCfg() const { return m_id2%sCfg; };\n", parser.m_cfgName.c_str(), parser.m_cfgName.c_str(), parser.m_cfgName.c_str());
	}
}

void ReplaceMutable(FILE* cppFile)
{
	for (SheetParser& parser : gVecSheetParser)
	{
		fprintf(cppFile, "	std::map<uint32_t, %sCfg>& GetMutable%sCfg() { return m_id2%sCfg; };\n", parser.m_cfgName.c_str(), parser.m_cfgName.c_str(), parser.m_cfgName.c_str());
	}
}

bool BuildCppEx(const string& templateName, const string& outName)
{//cpp file
	FILE* cppFile = nullptr;
	auto er = fopen_s(&cppFile, outName.c_str(), "w");
	if (er != 0)
	{
		LERROR("openfile fail %s", outName.c_str());
		return false;
	}
	
	{//read file CppTemplate.cpp
		FILE* tmpFile = nullptr;
		er = fopen_s(&tmpFile, templateName.c_str(), "r");
		if (er != 0)
		{
			LERROR("openfile CppTemplate.cpp fail");
			return false;
		}

		while (1) {
			char buf[1024];
			//每次调用文件指针fp会自动后移一行
			char* p = fgets(buf, 1024, tmpFile);
			if (!p)//文件读取结束则跳出循环
				break;
			bool bReplace = false;
			if ('-' == buf[0])
			{
				VecStr vecStr;
				const VecStr separatorList = { " ", "\n", "\r\n" };
				StrUtil::split(buf, separatorList, vecStr);
				if (vecStr.size()!=2)
				{
					LDEBUG("-define xxx error");
					exit(1);
				}

				auto it = gStr2fun.find(vecStr[1]);
				if (it == gStr2fun.end())
				{
					LERROR("main.cpp  and CppTemplate.xxx unicode is diffrent. %ld %s", vecStr[1].length(), vecStr[1].c_str());//查查main.cpp 和 CppTemplate.h 编码是不是不统一
					// for(auto&& [k,v]:gStr2fun)
					// {
					// 	LERROR("%s %ld", k.c_str(), k.length());
					// }
					exit(1);
				}
			//	LDEBUG("replace %s", vecStr[1].c_str());
				it->second(cppFile);
				bReplace = true;
			}
			if (!bReplace)
			{
				fputs(buf, cppFile);
			}
			//case fun, call func. else copy
		}
		fclose(tmpFile);
	}
	fclose(cppFile);
	return true;
}

bool BuildCpp()
{
	const char* cppPath = gCfg.GetStr("CppPath");

	{
		string name = cppPath;
		name.append(string(EXPORT_CPP_FILE) + ".cpp");
		if (!BuildCppEx("CppTemplate.cpp", name))
		{
			return false;
		}
	}
	{
		string name = cppPath;
		name.append(string(EXPORT_CPP_FILE) + ".h");
		if (!BuildCppEx("CppTemplate.h", name))
		{
			return false;
		}
	}
	return true;
}


/*
follow:
get all excel file path
read all excel file. read all sheet
parse sheet:
{
Parse struct
build cpp
build bytes
}
*/
int main()
{

	gCfg.init("toolCfg.txt");

	gStr2fun["InitCfg"] = ReplaceInitCfg;
	gStr2fun["getfunDef"] = ReplacegetfunDef;
	gStr2fun["declareInitCfg"] = ReplaceDeclareInitCfg;
	gStr2fun["callInitCfg"] = ReplaceCallInitCfg;
	gStr2fun["cfgstruct"] = ReplaceCfgStruct;
	gStr2fun["getfun"] = ReplaceGetFun;
	gStr2fun["member"] = ReplaceMember;
	gStr2fun["getall"] = ReplaceGetAll;
	gStr2fun["mutable"] = ReplaceMutable;


	IGNORE_CLIENT_FIELD_STRING = gCfg.GetStr("IGNORE_CLIENT_FIELD_STRING");; //忽略客户端字段
	EXPORT_CPP_FILE = gCfg.GetStr("EXPORT_CPP_FILE");; //导致CPP文件名前缀
	const char* excelPath = gCfg.GetStr("ExcelPath");
	LDEBUG("ExcelPath=%s", excelPath);
	vector<string> vecFilePath;
	TraverseFolder(excelPath, "xlsx", vecFilePath);

	for (auto& path : vecFilePath)
	{
		xlnt::workbook wb;
		if (*path.begin() == '~')//ignore tmp file
		{
			continue;
		}
		LDEBUG("file=%s", path.c_str());
		wb.load(excelPath+path);
		for (size_t i = 0; i < wb.sheet_count(); i++)
		{
			xlnt::worksheet ws = wb.sheet_by_index(i);
			cout << "sheet name=" << ws.title() << endl;
			SheetParser sheet(ws, path);
			xlnt::cell cell = ws.cell(xlnt::cell_reference(1, 1)); // 坐标定位
			if (StrUtil::tolower(cell.to_string()) != "id")
			{
			//	LDEBUG("cell(1,1) is not id, ignore %s", ws.title().c_str());
				continue;
			}
			sheet.Build();
			gVecSheetParser.push_back(sheet);
		}
	}
	CheckMgr::Ins().Check();
	BuildCpp();
	cout << "--------------build success--------------" << endl;
	//getchar();
	return 0;
}
